package in

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"strings"

	_ "github.com/mattn/go-sqlite3"
)

// "github.com/patrickmn/go-cache"

var CMD_PRINT = "cat"

type Bottle struct {
	Input    func(in interface{}) (io.Reader, error)
	Data     string `json: "data"`
	Output   string `json: "output"`
	Slice    string `json: "slice"`
	IsFile   bool   `json: "isFile"`
	IsBash   bool   `json: "isBash"`
	IsInner  bool   `json: "isInner"`
	Engine   interface{}
	IsDB     bool
	NoCached bool
	Tp       string
}

type S []string

type BottleInterface interface {
	Query(oper *Oper) Datas
	Desc(oper *Oper) S
	GetTp() string
	GetName() string
}

// Oper is a oper element to handle engine :
// now implemmented: mysql, sqlit
type Oper struct {
	Name   string
	Items  S
	Filter G
	Page   int
	Len    int
	Raw    string
	Sep    string
	Split  string
}

func OperWith(name string, items ...string) (soper *Oper) {
	soper = new(Oper)
	soper.Name = name
	soper.Items = items
	soper.Len = 10000
	return
}

func (oper *Oper) Set(items G) (soper *Oper) {
	for k, v := range items {
		if k == "len" {
			oper.Len = v.(int)
		} else if k == "page" {
			oper.Page = v.(int)
		} else if k == "name" {
			oper.Name = v.(string)
		} else if k == "raw" {
			oper.Raw = v.(string)
		} else if k == "sep" {
			oper.Sep = v.(string)
		} else if k == "split" {
			oper.Sep = v.(string)
		}
	}
	soper = oper
	return
}

// func (oper *BaseOper) Query(bottle *Bottle) (d Datas) {
// 	return
// }

func (oper *Oper) Grep(filter G, items ...string) (soper *Oper) {
	oper.Items = items
	oper.Filter = filter
	soper = oper
	return
}

func NewBottle(initInfo string) (bottle *Bottle) {
	if Exists(initInfo) {
		bottle = FileBottleParse(initInfo)
		return
	} else {
		bottle = EngineBottleParse(initInfo)
		return
	}
}

func EngineBottleParse(initInfo string) (bottle *Bottle) {
	return
}

func FileBottleParse(initInfo string) (bottle *Bottle) {
	bottle = new(Bottle)
	s := strings.Split(initInfo, ".")
	end := s[len(s)-1]

	bottle = &Bottle{
		Input: func(in interface{}) (io.Reader, error) {
			reader, err := ioutil.ReadFile(in.(string))
			return bytes.NewReader(reader), err
		},
		Data:    initInfo,
		IsFile:  true,
		IsInner: true,
	}
	switch end {
	case "csv":

	default:
		bottle.Slice = "\n"
	}
	return

}

// func (bottle *Bottle) GetKeys(table string) (keys []string, err error) {
// 	if bottle.Tp == "sqlite" {
// 		keysRe := regex.MustCompile(`\W(\w+)\W\s\w+,`)
// 		bottle.Query(G{
// 			"name": table,
// 		})
// 		keysRe.FindAllString()
// 	}
// }

func AutoBottle(schema string) (b BottleInterface, err error) {
	tps := strings.SplitN(schema, "://", 2)
	switch tps[0] {
	case "mysql":
		b, err = NewSqlBottle(schema)
		return
	case "sqlite3":
		b, err = NewSqlBottle(schema)
		return
	case "xlsx":
		b, err = NewXlsxBottle(schema)
		return
	case "text":
		b, err = NewTextBottle(schema)
		return
	case "mongodb":
		b, err = NewMongoBottle(schema)
	case "sql":
		b, err = NewSqlFileBottle(schema)
	default:
		err = errors.New("not supported schema:" + schema)
	}
	return
}

func LineToDict(line string) (d G) {
	d = make(G)
	for _, par := range strings.Split(line, ",") {
		if strings.Contains(par, "=") {
			vs := strings.SplitN(par, "=", 2)
			d[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1])
		} else if strings.Contains(par, ":") {
			vs := strings.SplitN(par, ":", 2)
			d[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1])
		} else {
			continue
		}
	}
	return
}
